package cn.fintecher.pangolin.service.manage.service;

import cn.fintecher.pangolin.common.enums.*;
import cn.fintecher.pangolin.common.exception.BadRequestException;
import cn.fintecher.pangolin.common.model.DepartmentModel;
import cn.fintecher.pangolin.common.model.ResourceModel;
import cn.fintecher.pangolin.common.model.UserModel;
import cn.fintecher.pangolin.common.response.LoginResponse;
import cn.fintecher.pangolin.common.utils.*;
import cn.fintecher.pangolin.entity.mysql.*;
import cn.fintecher.pangolin.service.manage.repository.*;
import cn.fintecher.pangolin.service.manage.request.*;
import cn.fintecher.pangolin.service.manage.response.SimpleUserResponse;
import cn.fintecher.pangolin.service.manage.response.UserDetailResponse;
import cn.fintecher.pangolin.service.manage.response.UserResponse;
import com.querydsl.core.BooleanBuilder;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.modelmapper.ModelMapper;
import org.modelmapper.TypeToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Auther: xiaqun
 * @Description:
 * @Date: 15:24 2019/8/13
 */

@Service("userService")
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private RedisTemplate<String, Object> jsonRedisTemplate;

    @Autowired
    private BaseService baseService;

    @Autowired
    private UserDetailRepository userDetailRepository;

    @Autowired
    private DepartmentRepository departmentRepository;

    @Autowired
    private SysParamRepository sysParamRepository;

    @Autowired
    private UserDeviceRepository userDeviceRepository;

    @Autowired
    private ComplianceCheckRepository complianceCheckRepository;

    @Autowired
    private CaseInfoRepository caseInfoRepository;

    @Autowired
    private AssistCaseRepository assistCaseRepository;

    @Autowired
    private FlowConfigurationRepository flowConfigurationRepository;

    private final static Integer removeResourceId = 311;

    /**
     * 用户登录
     */
    public LoginResponse login(HttpServletRequest request, LoginRequest loginRequest) {
        User user = validUser(loginRequest);
        //验证设备锁
//        validUserDevice(user.getId(), DeviceType.PC, loginRequest, request);
        //验证密码
        validPassword(loginRequest, user);
        //验证角色并生成返回值
        return validRole(user);
    }

    /**
     * 用户登出
     */
    @Transactional
    public void logout(String token) {
        jsonRedisTemplate.delete(token);
    }

    /**
     * APP用户登录
     */
    public LoginResponse appLogin(HttpServletRequest request, LoginRequest loginRequest) {
        User user = validUser(loginRequest);
        //验证设备锁
//        validUserDevice(user.getId(), DeviceType.APP, loginRequest, null);
        //TODO 登录日志
        //验证密码
        validPassword(loginRequest, user);
        //验证角色并生成返回值
        return validRole(user);
    }

    /**
     * 通过token获取用户信息
     */
    public LoginResponse getUserByToken(String token) {
        return (LoginResponse) jsonRedisTemplate.opsForValue().get(token);
    }

    /**
     * 新增用户
     */
    @Transactional
    public void addUser(AddUserRequest request, String token) {
        UserModel userModel = baseService.getUserByToken(token);
        boolean b = userRepository.exists(QUser.user.username.eq(request.getUsername()));
        if (b) {
            throw new BadRequestException(null, "user", "username.is.repeat");
        }
        //生成用户详细信息
        UserDetail userDetail = new UserDetail();
        BeanUtils.copyPropertiesIgnoreNull(request, userDetail);
        userDetail.process(userModel.getId(), ZWDateUtil.getNowDateTime(), true);
        if (StringUtils.isNotBlank(userDetail.getCertificateNo())) {
            String str = IdcardUtils.getGenderByIdCard(userDetail.getCertificateNo());
            if (Objects.nonNull(str)) {
                switch (str) {
                    case "男":
                        userDetail.setSex(Sex.MALE);
                        break;
                    case "女":
                        userDetail.setSex(Sex.FEMALE);
                        break;
                    case "未知":
                        userDetail.setSex(Sex.UNKNOWN);
                        break;
                }
            }
            userDetail.setAge(IdcardUtils.getAgeByIdCard(userDetail.getCertificateNo()));
        }
        UserDetail result = userDetailRepository.save(userDetail);
        //生成用户信息
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        User user = new User();
        BeanUtils.copyPropertiesIgnoreNull(request, user);
        user.setLockNumber(0); //锁定次数
        String password = getPassword();
        user.setPassword(passwordEncoder.encode(password)); //密码
        Department department = departmentRepository.findById(request.getDepartId()).orElseThrow(() -> new BadRequestException(null, "department", "department.is.null"));
        user.setDepartment(department); //所属部门
        user.setUserDetail(result); //用户详细信息
        user.process(userModel.getId(), ZWDateUtil.getNowDateTime(), true);
        user.setChannelNo(request.getChannelNo());
        user.setIsCheck(BaseSelect.NO);
        user.setIsSystem(BaseSelect.NO);
        userRepository.save(user);
    }

    /**
     * 删除用户
     */
    @Transactional
    public void deleteUser(String userId, LoginResponse loginResponse) {
        if (loginResponse.getUserModel().getIsManage().equals(BaseSelect.NO)) {
            throw new BadRequestException(null, "deleteUser", "user.is.not.manger");
        }
        BooleanBuilder builder = new BooleanBuilder();
        QCaseInfo caseInfo = QCaseInfo.caseInfo;
        builder.and(caseInfo.currentCollector.id.eq(userId));
        Iterable<CaseInfo> caseInfos = caseInfoRepository.findAll(builder);
        if (caseInfos.iterator().hasNext()) {
            throw new BadRequestException(null, "deleteUser", "user.has.case");
        }
        BooleanBuilder assistBuilder = new BooleanBuilder();
        assistBuilder.and(QAssistCase.assistCase.assistCollector.id.eq(userId));
        assistBuilder.and(QAssistCase.assistCase.assistStatus.ne(AssistStatus.ASSIST_WAIT_ASSIGN));
        Iterable<AssistCase> assistCases = assistCaseRepository.findAll(assistBuilder);
        if (assistCases.iterator().hasNext()) {
            throw new BadRequestException(null, "deleteUser", "user.has.assistCase");
        }
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        booleanBuilder.and(QFlowConfiguration.flowConfiguration.flowNode.any().approverConfigs.any().userId.eq(userId));
        Iterable<FlowConfiguration> flowConfig = flowConfigurationRepository.findAll(booleanBuilder);
        if (flowConfig.iterator().hasNext()) {
            throw new BadRequestException(null, "deleteUser", "user.has.flowConfig");
        }
        User user = userRepository.findById(userId).orElseThrow(() -> new BadRequestException(null, "user", "user.is.null"));
        if (user.getIsSystem().equals(BaseSelect.YES)) {
            throw new BadRequestException(null, "user", "system.cant.delete");
        }
        if (Objects.nonNull(user.getUserDetail())) {
            userDetailRepository.delete(user.getUserDetail());
        }
        userRepository.delete(user);
    }

    /**
     * 修改用户
     */
    @Transactional
    public void modifyUser(ModifyUserRequest request, String token) {
        UserModel userModel = baseService.getUserByToken(token);
        User user = userRepository.findById(request.getId()).orElseThrow(() -> new BadRequestException(null, "user", "user.is.null"));
        if (user.getIsSystem().equals(BaseSelect.YES)) {
            throw new BadRequestException(null, "user", "system.cant.modify");
        }
        if (!Objects.equals(request.getStatus(), user.getStatus()) && request.getStatus().equals(UserStatus.LEAVE)) {
            BooleanBuilder builder = new BooleanBuilder();
            QCaseInfo caseInfo = QCaseInfo.caseInfo;
            builder.and(caseInfo.currentCollector.id.eq(request.getId()));
            Iterable<CaseInfo> caseInfos = caseInfoRepository.findAll(builder);
            if (caseInfos.iterator().hasNext()) {
                throw new BadRequestException(null, "modifyUser", "user.modify.has.case");
            }
            BooleanBuilder assistBuilder = new BooleanBuilder();
            assistBuilder.and(QAssistCase.assistCase.assistCollector.id.eq(request.getId()));
            assistBuilder.and(QAssistCase.assistCase.assistStatus.eq(AssistStatus.ASSIST_COLLECTING));
            Iterable<AssistCase> assistCases = assistCaseRepository.findAll(assistBuilder);
            if (assistCases.iterator().hasNext()) {
                throw new BadRequestException(null, "modifyUser", "user.modify.has.assistCase");
            }
            BooleanBuilder booleanBuilder = new BooleanBuilder();
            booleanBuilder.and(QFlowConfiguration.flowConfiguration.flowNode.any().approverConfigs.any().userId.eq(request.getId()));
            Iterable<FlowConfiguration> flowConfig = flowConfigurationRepository.findAll(booleanBuilder);
            if (flowConfig.iterator().hasNext()) {
                throw new BadRequestException(null, "modifyUser", "user.modify.has.flowConfig");
            }
        }
        //TODO 处理改变部门
        UserDetail userDetail = user.getUserDetail();
        if (Objects.isNull(userDetail)) {
            userDetail = new UserDetail();
        }
        userDetail.setCertificateNo(request.getCertificateNo());
        userDetail.setAge(request.getAge());
        userDetail.setSex(request.getSex());
        userDetail.setBirthplace(request.getBirthplace());
        userDetail.setNation(request.getNation());
        userDetail.setProfession(request.getProfession());
        userDetail.setPoliticalStatus(request.getPoliticalStatus());
        userDetail.setMaritalStatus(request.getMaritalStatus());
        userDetail.setEducation(request.getEducation());
        userDetail.setProfession(request.getProfession());
        userDetail.setSchool(request.getSchool());
        userDetail.setGraduationTime(request.getGraduationTime());
        userDetail.setJoinWorkTime(request.getJoinWorkTime());
        userDetail.setPhoneNo(request.getPhoneNo());
        userDetail.setEmail(request.getEmail());
        userDetail.setExperience(request.getExperience());
        userDetail.setLastEmployer(request.getLastEmployer());
        userDetail.setEmergencyContactName(request.getEmergencyContactName());
        userDetail.setEmergencyContactNo(request.getEmergencyContactNo());
        userDetail.setHomeAddress(request.getHomeAddress());
        userDetail.setResidenceAddress(request.getResidenceAddress());
        userDetail.setEntryTime(request.getEntryTime());
        userDetail.setWorkAge(request.getWorkAge());
        userDetail.setInsurance(request.getInsurance());
        userDetail.setBank(request.getBank());
        userDetail.setBankCard(request.getBankCard());
        userDetail.setFirstSignDate(request.getFirstSignDate());
        userDetail.setFirstExpireDate(request.getFirstExpireDate());
        userDetail.setSecondSignDate(request.getSecondSignDate());
        userDetail.setSecondExpireDate(request.getSecondExpireDate());
        userDetail.setThirdSignDate(request.getThirdSignDate());
        userDetail.setLeaveDate(request.getLeaveDate());
        userDetail.setLeaveReason(request.getLeaveReason());
        userDetail.setRemark(request.getRemark());
        userDetail.process(userModel.getId(), ZWDateUtil.getNowDateTime(), false);
        UserDetail result = userDetailRepository.save(userDetail);
        Department department = departmentRepository.findById(request.getDepartId()).orElseThrow(() -> new BadRequestException(null, "department", "department.is.null"));
        user.setRealName(request.getRealName());
        user.setDepartment(department); //所属部门
        user.setIsManage(request.getIsManage());
        user.setStatus(request.getStatus());
        user.setStation(request.getStation());
        user.setUserDetail(result); //用户详细信息
        user.process(userModel.getId(), ZWDateUtil.getNowDateTime(), false);
        user.setChannelNo(request.getChannelNo());
        userRepository.save(user);
    }

    /**
     * 重置/修改密码
     */
    @Transactional
    public void resetPassword(ResetPasswordRequest request, String token) {
        UserModel userModel = baseService.getUserByToken(token);
        Iterable<User> iterable = userRepository.findAllById(request.getUserIds());
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        iterable.forEach(e -> {
            if (StringUtils.isNotBlank(request.getPassword())) { //修改
                e.setPassword(passwordEncoder.encode(request.getPassword()));
            } else { //重置
                String password = getPassword();
                e.setPassword(passwordEncoder.encode(password));
            }
            e.process(userModel.getId(), ZWDateUtil.getNowDateTime(), false);
        });
        userRepository.saveAll(iterable);
    }

    /**
     * 获取系统参数设置的默认密码
     */
    private String getPassword() {
        SysParam sysParam = sysParamRepository.findOne(QSysParam.sysParam.code.eq(Constants.DEFAULT_PASSWORD)).orElseThrow(() -> new BadRequestException(null, "sysParam", "sysparam.is.null"));
        if (Objects.equals(sysParam.getStatus(), BaseStatus.OFF)) {
            throw new BadRequestException(null, "sysParam", "sysparam.is.unavailable");
        }
        if (StringUtils.isBlank(sysParam.getValue())) {
            throw new BadRequestException(null, "sysParam", "sysparam.value.is.empty");
        }
        return sysParam.getValue();
    }

    /**
     * 多条件查询用户
     */
    public Page<UserResponse> findUserByDepartment(FindUserRequest request, Pageable pageable) {
        Sort sort = new Sort(Sort.Direction.DESC, "operateTime");
        pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
        Page<User> page = userRepository.findAll(request.getBuilder(), pageable);
        return page.map(e -> modelMapper.map(e, UserResponse.class));
    }

    /**
     * @Author wangxiangdong
     * @Description 根据部门code查询用户
     * @Date 2019/9/4 14:36
     **/
    public Page<UserResponse> findUserByDepartmentId(String departmentCode, Pageable pageable) {
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        booleanBuilder.and(QUser.user.department.departCode.like(StringUtils.join(departmentCode, "%")));
        Page<User> page = userRepository.findAll(booleanBuilder, pageable);
        return page.map(e -> modelMapper.map(e, UserResponse.class));
    }

    /**
     * 查询用户详情
     */
    public UserDetailResponse getUserDetail(String userId) {
        User user = userRepository.findById(userId).orElseThrow(() -> new BadRequestException(null, "user", "user.is.null"));
        return modelMapper.map(user, UserDetailResponse.class);
    }

    /**
     * 登陆用户验证
     */
    private User validUser(LoginRequest loginRequest) {
        User user = userRepository.findOne(QUser.user.username.eq(loginRequest.getUsername())).orElseThrow(() -> new BadRequestException(null, "login", "user.is.null"));
        if (Objects.equals(user.getStatus(), UserStatus.LEAVE)) { //离职
            throw new BadRequestException(null, "login", "user.is.leaved");
        }
        if (Objects.equals(user.getStatus(), UserStatus.LOCKING)) {
            throw new BadRequestException(null, "login", "account.is.lock");
        }
        return user;
    }

    /**
     * 登录设备锁验证
     */
    private void validUserDevice(String userId, DeviceType deviceType, LoginRequest loginRequest, HttpServletRequest request) {
        Optional<UserDevice> optional = userDeviceRepository.findOne(QUserDevice.userDevice.deviceType.eq(deviceType));
        if (optional.isPresent()) {
            UserDevice userDevice = optional.get();
            if (Objects.equals(userDevice.getStatus(), BaseStatus.ON)) {
                boolean b = false;
                if (Objects.equals(deviceType, DeviceType.PC)) { //PC登录
                    if (!Objects.equals(userDevice.getMac(), baseService.getAddressIP(request))) {
                        b = true;
                    }
                } else { //APP登录
                    if (!Objects.equals(loginRequest.getDeviceCode(), userDevice.getDeviceCode())) {
                        b = true;
                    }
                }
                if (b) {
                    throw new BadRequestException(null, "userDevice", "userdevice.is.not.match");
                }
            }
        } else {
            UserDevice userDevice = new UserDevice();
            userDevice.setUserId(userId);
            userDevice.setDeviceType(deviceType);
            userDevice.setStatus(BaseStatus.ON);
            userDevice.setDeviceCode(loginRequest.getDeviceCode());
            userDevice.setMac(baseService.getAddressIP(request));
            userDeviceRepository.save(userDevice);
        }
    }

    /**
     * 登录密码验证
     */
    private void validPassword(LoginRequest loginRequest, User user) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {//密码不正确
            user.setLockNumber(Objects.nonNull(user.getLockNumber()) ? (user.getLockNumber() + 1) : 1);
            userRepository.save(user);
            if (user.getLockNumber() == 5) {
                user.setStatus(UserStatus.LOCKING);
                userRepository.save(user);
                throw new BadRequestException(null, "login", "account.is.lock");
            } else {
                throw new BadRequestException(null, "login", "password.is.wrong");
            }
        } else {
            user.setLockNumber(0);
            userRepository.save(user);
        }
        if (Objects.isNull(user.getRoles()) || user.getRoles().isEmpty()) {//没有角色
            throw new BadRequestException(null, "login", "role.is.empty");
        } else {
            user.getRoles().forEach(e -> {
                if (Objects.equals(e.getStatus(), BaseStatus.OFF)) {
                    throw new BadRequestException(null, "login", "role.is.empty");
                }
            });
        }
    }

    /**
     * 登录验证角色
     */
    private LoginResponse validRole(User user) {
        Set<Resource> menu = new HashSet<>();
        Set<String> roleIds = new HashSet<>();
        Set<Integer> resource = new HashSet<>();
        UserModel userModel = modelMapper.map(user, UserModel.class);
        Type listType = new TypeToken<Set<ResourceModel>>() {
        }.getType();
        //是否开启合规检查
        if (Objects.equals(user.getIsCheck(), BaseSelect.YES)) {
            BooleanBuilder booleanBuilder = new BooleanBuilder();
            booleanBuilder.and(QComplianceCheck.complianceCheck.target.any().userId.eq(user.getId()));
            Iterator<ComplianceCheck> complianceCheckIterator = complianceCheckRepository.findAll(booleanBuilder).iterator();
            Set<String> compliancePrincipalIds = new HashSet<>();
            Set<Resource> complianceResources = new HashSet<>();
            while (complianceCheckIterator.hasNext()) {
                ComplianceCheck complianceCheck = complianceCheckIterator.next();
                Set<SimplePrincipal> principals = complianceCheck.getPrincipals();
                compliancePrincipalIds.addAll(principals.stream().map(SimplePrincipal::getPrincipalId).collect(Collectors.toSet()));
                userModel.setPrincipalIds(compliancePrincipalIds);
                if (!complianceCheck.getResources().isEmpty()) {
                    //查找ID为311的资源 去掉
                    Set<Resource> collect = complianceCheck.getResources().stream().filter(x -> Objects.equals(x.getId(), removeResourceId)).collect(Collectors.toSet());
                    if (collect.size() > 0) {
                        complianceCheck.getResources().removeAll(collect);
                    }
                    complianceResources.addAll(complianceCheck.getResources());
                    userModel.setResource(complianceResources.stream().map(Resource::getId).collect(Collectors.toSet()));
                    userModel.setMenu(modelMapper.map(complianceResources, listType));
                } else {
                    setResource(user.getRoles(), userModel, roleIds, resource, menu, listType);
                }
            }
        } else {
            setResource(user.getRoles(), userModel, roleIds, resource, menu, listType);
        }
        DepartmentModel departmentModel = modelMapper.map(user.getDepartment(), DepartmentModel.class);
        String token = MD5.MD5Encode(user.getUsername());
        LoginResponse response = new LoginResponse();
        response.setUserModel(userModel);
        response.setDepartmentModel(departmentModel);
        response.setToken(token);
        jsonRedisTemplate.opsForValue().set(token, response);
        return response;
    }

    private void setResource(Set<Role> roles, UserModel userModel, Set<String> roleIds, Set<Integer> resource, Set<Resource> menu, Type listType) {
        roles.forEach(e -> {
            roleIds.add(e.getId());
            Set<Resource> collect = e.getResources().stream().filter(x -> Objects.equals(x.getId(), removeResourceId)).collect(Collectors.toSet());
            if (collect.size() > 0) {
                e.getResources().removeAll(collect);
            }
            menu.addAll(e.getResources());
            e.getResources().forEach(resource1 -> resource.add(resource1.getId()));
        });
        if (menu.isEmpty()) { //没有角色
            throw new BadRequestException(null, "login", "resource.is.empty");
        }
        userModel.setMenu(modelMapper.map(menu, listType));
        userModel.setRoleIds(roleIds);
        userModel.setResource(resource);
    }

    /**
     * 查询用户所分配角色
     */
    public Set<String> getRoles(String userId) {
        User user = userRepository.findById(userId).orElseThrow(() -> new BadRequestException(null, "user", "user.is.null"));
        Set<String> set = new HashSet<>();
        user.getRoles().forEach(e -> set.add(e.getId()));
        return set;
    }

    /**
     * 用户解锁
     */
    public void unlock(String userId, String token) {
        UserModel userModel = baseService.getUserByToken(token);
        User user = userRepository.findById(userId).orElseThrow(() -> new BadRequestException(null, "", "user.is.null"));
        user.setLockNumber(0);
        user.setStatus(UserStatus.WORKING);
        user.process(userModel.getId(), ZWDateUtil.getNowDateTime(), false);
        userRepository.save(user);
    }

    /**
     * 一键解锁
     */
    @Transactional
    public void batchUnlock(String token) {
        UserModel userModel = baseService.getUserByToken(token);
        Iterable<User> iterable = userRepository.findAll();
        iterable.forEach(e -> {
            e.setLockNumber(0);
            e.setStatus(UserStatus.WORKING);
            e.process(userModel.getId(), ZWDateUtil.getNowDateTime(), false);
        });
        userRepository.saveAll(iterable);
    }


    @Transactional
    public void updatePassword(UpdatePasswordRequest request, UserModel userModel) {
        User user = userRepository.findById(userModel.getId()).orElseThrow(() -> new BadRequestException(null, "", "user.is.null"));
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if (!passwordEncoder.matches(request.getOldPassword(), user.getPassword())) {//密码不正确
            throw new BadRequestException(null, "", "old.password.is.error");
        }
        if (!Objects.equals(request.getNewPassword(), request.getConfirmPassword())) {
            throw new BadRequestException(null, "", "password.is.unlike");
        }
        user.setPassword(passwordEncoder.encode(request.getNewPassword()));
        userRepository.save(user);
    }

    /**
     * 获取部门下用户
     */
    public List<SimpleUserResponse> getSimpleUser(String departCode) {
        Iterable<User> iterable = userRepository.findAll(QUser.user.department.departCode.eq(departCode)
                .and(QUser.user.status.ne(UserStatus.LEAVE)).and(QUser.user.isSystem.eq(BaseSelect.NO)));
        List<User> list = IterableUtils.toList(iterable);
        Type listType = new TypeToken<List<SimpleUserResponse>>() {
        }.getType();
        return modelMapper.map(list, listType);
    }
}
